package com.qire.antscore.common;

import java.util.List;

/**
 * DeepLink URI 分析工具
 */
public class DeepLinkUtils {

    /**
     * 跳过检索的 {@code urlPath} 路径头部空白，从 {@code pos} 开始递增直到 {@code limit} 为止，如果遇到 {@code urlPath[pos]} 不为 ASCII 空白则返回当前位置。
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @return 不为空白的起始位置。
     */
    public static int skipLeadingAsciiWhitespace(String urlPath, int pos, int limit) {
        for (int i = pos; i < limit; i++) {
            switch (urlPath.charAt(i)) {
                case '\t':
                case '\n':
                case '\f':
                case '\r':
                case ' ':
                    continue;
                default:
                    return i;
            }
        }
        return limit;
    }

    /**
     * 跳过检索的 {@code urlPath} 路径尾部空白位置，从 {@code limit} 开始递减直到 {@code pos} 为止，如果遇到 {@code urlPath[limit - 1]} 不为 ASCII 空白则返回当前位置。
     * @param urlPath 检索的url路径
     * @param pos 检索结束位置
     * @param limit 检索起始位置
     * @return 不为空白的尾部位置
     */
    public static int skipTrailingAsciiWhitespace(String urlPath, int pos, int limit) {
        for (int i = limit - 1; i >= pos; i--) {
            switch (urlPath.charAt(i)) {
                case '\t':
                case '\n':
                case '\f':
                case '\r':
                case ' ':
                    continue;
                default:
                    return i + 1;
            }
        }
        return pos;
    }

    /**
     * 返回 scheme 字符串到 ":" 位置在 {@code urlPath} 中的索引位置，如果从 {@code pos} 位置开始不存在 scheme 则返回 -1 。
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @return 返回 scheme 后 ":" 所在 {@code urlPath} 中的索引位置
     */
    public static int schemeDelimiterOffset(String urlPath, int pos, int limit) {
        if (limit - pos < 2) return -1;

        char c0 = urlPath.charAt(pos);
        if ((c0 < 'a' || c0 > 'z') && (c0 < 'A' || c0 > 'Z')) return -1; // Not a scheme start char.

        for (int i = pos + 1; i < limit; i++) {
            char c = urlPath.charAt(i);

            if ((c >= 'a' && c <= 'z')
                    || (c >= 'A' && c <= 'Z')
                    || (c >= '0' && c <= '9')
                    || c == '+'
                    || c == '-'
                    || c == '.') {
                continue; // Scheme character. Keep going.
            } else if (c == ':') {
                return i; // Scheme prefix!
            } else {
                return -1; // Non-scheme character before the first ':'.
            }
        }

        return -1; // No ':'; doesn't start with a scheme.
    }

    /**
     * 返回从 {@code pos} 开始 {@code urlPath} 中 '/' 和 '\' 的数量。
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @return 返回 '/' 和 '\' 的数量。
     */
    public static int slashCount(String urlPath, int pos, int limit) {
        int slashCount = 0;
        while (pos < limit) {
            char c = urlPath.charAt(pos);
            if (c == '\\' || c == '/') {
                slashCount++;
                pos++;
            } else {
                break;
            }
        }
        return slashCount;
    }

    /**
     * 返回从 {@code pos} 开始 {@code delimiters} 任意一个字符在 {@code urlPath} 中第一次出现的位置索引。如果没有出现，则返回limit。
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @param delimiters 定界符组
     * @return
     */
    public static int delimiterOffset(String urlPath, int pos, int limit, String delimiters) {
        for (int i = pos; i < limit; i++) {
            if (delimiters.indexOf(urlPath.charAt(i)) != -1) return i;
        }
        return limit;
    }

    /**
     * 从 {@code pos} 位置开始查找 {@code urlPath} 中跳过 "[...]" 方括号之间的字符的第一个 ':' 的位置
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @return 返回端口好的位置
     */
    public static int portColonOffset(String urlPath, int pos, int limit) {
        for (int i = pos; i < limit; i++) {
            switch (urlPath.charAt(i)) {
                // 处理端口号前需要处理IPV6的主机名格式，如 [1111:2222:3333:4444:5555:6666:7777:8888] or [::1]
                case '[':
                    while (++i < limit) {
                        if (urlPath.charAt(i) == ']') break;
                    }
                    break;
                case ':':
                    return i;
                default:
                    break;
            }
        }
        return limit; // No colon.
    }

    /**
     * 获取 Host
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @return
     */
    public static String canonicalizeHost(String urlPath, int pos, int limit) {
        String percentDecoded = urlPath.substring(pos, limit);
        return percentDecoded;
    }

    /**
     * 获取路径
     * @param pathSegmentList 存放接收路径片段的列表
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @return
     */
    public static void resolvePath(List<String> pathSegmentList, String urlPath, int pos, int limit) {

        // Read a delimiter.
        if (pos == limit) {
            // Empty path: keep the base path as-is.
            return;
        }
        char c = urlPath.charAt(pos);
        if (c == '/' || c == '\\') {
            // Absolute path: reset to the default "/".
            pathSegmentList.clear();
            pathSegmentList.add("");
            pos++;
        } else {
            // Relative path: clear everything after the last '/'.
            pathSegmentList.set(pathSegmentList.size() - 1, "");
        }

        // Read path segments.
        for (int i = pos; i < limit;) {
            int pathSegmentDelimiterOffset = delimiterOffset(urlPath, i, limit, "/\\");
            boolean segmentHasTrailingSlash = pathSegmentDelimiterOffset < limit;
            push(pathSegmentList, urlPath, i, pathSegmentDelimiterOffset, segmentHasTrailingSlash, true);
            i = pathSegmentDelimiterOffset;
            if (segmentHasTrailingSlash) i++;
        }

        return;
    }

    /**
     * 添加路径段。如果urlPath是 ".." 或等效名称，则会弹出路径段。
     * @param pathSegmentList 存放接收路径片段的列表
     * @param urlPath 检索的url路径
     * @param pos 检索起始位置
     * @param limit 检索结束位置
     * @param addTrailingSlash 尾部是否追加斜杠
     * @param alreadyEncoded 已经编码
     */
    private static void push(List<String> pathSegmentList, String urlPath, int pos, int limit, boolean addTrailingSlash, boolean alreadyEncoded) {
//        String segment = canonicalize(urlPath, pos, limit, PATH_SEGMENT_ENCODE_SET, alreadyEncoded, false);
        String segment = urlPath.substring(pos);
        if (isDot(segment)) {
            return; // Skip '.' path segments.
        }
        if (isDotDot(segment)) {
            pop(pathSegmentList);
            return;
        }
        if (pathSegmentList.get(pathSegmentList.size() - 1).isEmpty()) {
            pathSegmentList.set(pathSegmentList.size() - 1, segment);
        } else {
            pathSegmentList.add(segment);
        }
        if (addTrailingSlash) {
            pathSegmentList.add("");
        }
    }
    /**
     * 移除一个路径片段，并始终保持最后一个片段为 ""，这是为了编码路径始终带有一个 '/'结尾，例如：
     *
     * <p>弹出 "/a/b/c/" 中的一个片段后变成 "/a/b/". 这样 {@code pathSegmentList} 路径片段列表将从 ["a", "b", "c", ""] 变为 ["a", "b", ""].
     *
     * <p>弹出 "/a/b/c" 中的一个片段后也会变成 "/a/b/". 这样 {@code pathSegmentList} 路径片段列表将从 ["a", "b", "c"] 变为 ["a", "b", ""].
     */
    private static void pop(List<String> pathSegmentList) {
        String removed = pathSegmentList.remove(pathSegmentList.size() - 1);

        // Make sure the path ends with a '/' by either adding an empty string or clearing a segment.
        if (removed.isEmpty() && !pathSegmentList.isEmpty()) {
            pathSegmentList.set(pathSegmentList.size() - 1, "");
        } else {
            pathSegmentList.add("");
        }
    }

    private static boolean isDot(String input) {
        return input.equals(".") || input.equalsIgnoreCase("%2e");
    }

    private static boolean isDotDot(String input) {
        return input.equals("..")
                || input.equalsIgnoreCase("%2e.")
                || input.equalsIgnoreCase(".%2e")
                || input.equalsIgnoreCase("%2e%2e");
    }


}
